// Object 类型(任何的非原始类型)
const a: object = function () {};
const b: { b: number } = { b: 1 };

// 数组类型
const arr: Array<string> = ['a', 'b', 'c'];
const arr2: number[] = [1, 2, 3];
// 元祖（有固定长度和类型的数组）
const tuple: [string, number] = ['a', 1];

// 枚举(Enum)
// ------------ 双向键值对对象
enum PostStatus {
  Dreft = 0,
  Unpublished = 1,
  Published = 2,
}
// PostStatus[0] 'Dreft'
enum PostStatus2 {
  Dreft = 3,
  Unpublished, // 4
  Published, // 5
}
// ------------ 常量枚举
const enum PostStatus3 {
  Dreft = 3,
  Unpublished, // 4
  Published, // 5
}
// PostStatus3[0] undefined

const post = {
  title: 'Hello',
  content: 'this is content',
  status: PostStatus.Dreft,
};

/**
 * 函数类型
 */
function fn1(x: number, y: string, z = 'z') {
  console.log(x, y, z);
}
fn1(1, '2'); // 1 '2' 'Z'

const fn2: (x: number, y: string, z?: string, ...rest: any[]) => void = (
  x: number,
  y: string,
  z?: string,
  ...rest: any[]
) => {
  console.log(x, y, z, ...rest);
};
fn2(1, '2'); // 1 '2' undefined
fn2(3, '4', '5', 6); // 3 '4' '5' 6

// 任意类型 any
let any1: any;
any1 = 1;
any1 = 'a';

// 隐式类型
let any2 = 1;
// any2 = 'a'  // error

// 类型断言
const arr3 = [1, 2, 3];
const res = arr3.find((e) => e > 2);
// const square = res * res // error
const square1 = (res as number) * (res as number);
const square2 = <number>res * <number>res; // jsx 下不可使用

/**
 * 接口 interface
 */
interface Post {
  title: string;
  content: string;
  subtitle?: string;
  readonly summary: string; // 只读成员
  [props: string]: string | undefined; // 动态成员
}
function printPost(post: Post) {
  console.log(post.title);
  console.log(post.content);
  console.log(post.subtitle);
  console.log(post.summary);
}
printPost({
  title: 'title',
  content: 'content',
  summary: 'haha',
  test: 'test',
});
// title
// content
// haha
// test

/**
 * 类 Class
 *
 * 类的访问修饰符
 * public     共有属性：内外都可访问
 * private    私有属性：只有类内部可访问
 * protected  保护属性：只有类和子类的内部可访问
 *
 */
class Person {
  // 需要先声明，且在构造器函数中赋值
  name: string;
  private age: number;
  protected readonly gender: 'man' | 'woman' = 'man';

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
class Student extends Person {
  private constructor(name: string, age: number) {
    super(name, age);
    console.log(this.gender); // man
  }
  static creat(name: string, age: number) {
    return new Student(name, age);
  }
}
const user = new Person('Lin', 18);
console.log(user.name); // Lin
// console.log(user.age);  // error

// const student = new Student('wang', 18)  // error 构造函数是private所以外部无法访问
const student = Student.creat('wang', 18); // ok

/**
 * 类与接口
 */
interface Run {
  run(distance: number): void;
}
interface Eat {
  eat(food: string): void;
}

class Human implements Run, Eat {
  run(distance: number): void {
    console.log('Human run:', distance);
  }
  eat(food: string): void {
    console.log('Human eat:', food);
  }
}
class Animal implements Run, Eat {
  run(distance: number): void {
    console.log('Animal run:', distance);
  }
  eat(food: string): void {
    console.log('Animal eat:', food);
  }
}

/**
 * 抽象类
 */
// 抽象类不能被实例化，只能够被子类继承
abstract class Animal2 {
  eat(food: string): void {
    console.log('Animal eat:', food);
  }
  abstract run(distance: number): void;
  // NOTE 抽象方法不能有具体实现，但是子类必须实现父类中定义的抽象方法
}
class Dog extends Animal2 {
  run(distance: number): void {
    console.log('Dog run:', distance);
  }
}
const dog = new Dog();
dog.eat('shit');
dog.run(123);

/**
 * 泛型
 */
function creatNumberArray(length: number, value: number): number[] {
  return Array<number>(length).fill(value);
}
console.log('creatNumberArray:', creatNumberArray(3, 1));
function creatStringArray(length: number, value: string): string[] {
  return Array<string>(length).fill(value);
}
console.log('creatStringArray:', creatStringArray(3, 'a'));
function creatArray<T>(length: number, value: T): T[] {
  return Array<T>(length).fill(value);
}
console.log('creatArray:', creatArray<number>(3, 2));
console.log('creatArray:', creatArray<string>(3, 'b'));

/**
 * 类型声明，类型声明模块
 */
// declare function camelCase (value: string): string
// @types/lodash
import { camelCase } from 'lodash';
console.log('camelCase:', camelCase('hello world'));
